package wekaGrexBridge;

import grex.BNFException;
import grex.Environment;
import grex.GP;
import grex.Nodes.Node;
import grex.Nodes.GeneException;

import java.util.ArrayList;
import java.util.StringTokenizer;

import weka.classifiers.Classifier;
import weka.classifiers.trees.J48;
import weka.core.Instances;

public class J48Injection extends ModelInjection {

    @Override
    public Classifier buildModel(Instances instances, ArrayList<Integer> indices,
            Object... optionalParameters) throws Exception {
        J48 tree = new J48();
        Instances newData = new Instances(instances, indices.size());
        for (int i = 0; i < indices.size(); i++) {
            newData.add(instances.instance(indices.get(i)));
        }

        if (optionalParameters.length > 0) {
            tree.setConfidenceFactor((Float) optionalParameters[0]);
        } else {
            throw new Exception("Coincidence Factor (float) is not provided as a parameter!");
        }
        tree.buildClassifier(newData);

        return tree;
    }
    
    public Classifier buildModel(Instances instances, Object... optionalParameters) throws Exception {
        J48 tree = new J48();
        tree.setBinarySplits(true);
        tree.setMinNumObj(2);

        if (optionalParameters.length > 0) {
            tree.setConfidenceFactor((Float) optionalParameters[0]);
        } else {
            throw new Exception("Coincidence Factor (float) is not provided as a parameter!");
        }
        tree.buildClassifier(instances);
       
        return tree;
    }


    @Override
    public GP parseModel(Classifier classifier, Environment environment) {
        this.environment = environment;
        try {
            if(classifier instanceof J48){
                GP gp = getTreeAsGP(classifier.toString(), environment);
                return gp;
            }
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /*public GP translateJ48toGP(Classifier tree) {
        String s = tree.toString();
        System.out.println("\n" + s + "\n");
        return null;
    }*/

    public Node translateLine(StringTokenizer line, Environment env) throws BNFException {
        Node gene, geneIf, geneLeftchild, geneRightChild;
        String currentLine = line.nextToken();
        StringTokenizer term = new StringTokenizer(currentLine, " |");
        String column = term.nextToken();
        String operator = term.nextToken();
        String value = term.nextToken(":").trim();
        if (operator.equals("<=") || operator.equals("=")) { //we have a new if followed by the true case (left branch)
            Node geneCondition = createCondition(column, operator, value, env);
            if (term.hasMoreTokens()) { //The If is followed by a prediction
                geneLeftchild = createPredictionGene(env);
            } else {//The if is follwed by onther if
                geneLeftchild = translateLine(line, env);
            }
            geneRightChild = translateLine(line, env);
            return createIfGene(geneCondition, geneLeftchild, geneRightChild, env);
        } else {//the false case of the  if (right bracn) (Don't create a new if)
            if (term.hasMoreTokens()) //The If is followed by a prediction and thus terminates the recursion
            {
                return createPredictionGene(env);
            }
            return translateLine(line, env);
        }
    }

    public GP getTreeAsGP(String j48Tree, Environment environment) throws BNFException, GeneException {

        if (environment.getPredictorClass().isEmpty()) {
            throw new BNFException("When Starting from a Decisoin tree the predictor class must be defined in the BNF using: \"terminalPredictor\"");
        }

        StringTokenizer st = new StringTokenizer(j48Tree, "\n");
        st.nextToken(); //J48 pruned tree
        st.nextToken(); //------------------
       // st.nextToken(); //Empty line
        Node root = translateLine(st, environment);

        GP gp = new GP(environment);
        gp = new GP(environment, root);
        gp.train();

        return gp;
    }
}
